package concert;

import java.util.ArrayList;

import JaCoP.constraints.Alldiff;
import JaCoP.constraints.And;
import JaCoP.constraints.Diff2;
import JaCoP.constraints.IfThenElse;
import JaCoP.constraints.Reified;
import JaCoP.constraints.Sum;
import JaCoP.constraints.SumWeight;
import JaCoP.constraints.XeqC;
import JaCoP.constraints.XeqY;
import JaCoP.constraints.XgteqY;
import JaCoP.constraints.XlteqY;
import JaCoP.constraints.XplusCeqZ;
import JaCoP.constraints.XplusYeqZ;
import JaCoP.core.IntVar;
import JaCoP.core.Store;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.InputOrderSelect;
import JaCoP.search.MostConstrainedDynamic;
import JaCoP.search.PrintOutListener;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleMatrixSelect;
import JaCoP.search.SimpleSelect;
import JaCoP.search.SmallestDomain;

public class Concert {
	protected static final int LENGTH_OF_SCHEDULE = 33;
	protected static final int MATRIX_COLUMN = 4;
	protected static final int NBR_OF_PLAYERS = 5;
	protected static final int NBR_OF_PIECES = 9;
	protected static final int[] DURATIONS = { 2, 4, 1, 3, 3, 2, 5, 7, 6 };
	protected int[] minPlayerSchedules;
	protected Diff2 playSchedule;
	protected IntVar sumOfSchedule; // use to minimize
	protected Store store;
	protected IntVar[][] pieces, players;
	protected ArrayList<IntVar> searchVars;
	protected ArrayList<IntVar> waitingTimes;

	public Concert() {
		store = new Store();
		searchVars = new ArrayList<IntVar>();
		waitingTimes = new ArrayList<IntVar>();
		minPlayerSchedules = new int[NBR_OF_PLAYERS];
		for (int i = 0; i < minPlayerSchedules.length; i++) {
			minPlayerSchedules[i] = 0;
		}
		// Define the constant values
		int[][] values = { new int[] { 1, 1, 0, 1, 0, 1, 1, 0, 1 },
				new int[] { 1, 1, 0, 1, 1, 1, 0, 1, 0 },
				new int[] { 1, 1, 0, 0, 0, 0, 1, 1, 0 },
				new int[] { 1, 0, 0, 0, 1, 1, 0, 0, 1 },
				new int[] { 0, 0, 1, 0, 1, 1, 1, 1, 0 } };
		IntVar[][] allRectangles = setupPlaySchedule();
		genConstraintsToTrapPieces(values);
		IntVar totalWaitingTime = defineTotalWaitingTimeVar();
		if (!store.consistency()) {
			System.out.println("Is inconsistent");
		}

		IntVar[] tmp = new IntVar[searchVars.size()];
		searchVars.toArray(tmp);
		Search<IntVar> search = new DepthFirstSearch<IntVar>(); // DFS
		System.out.println("-.--------");
		search.setSolutionListener(new PrintOutListener<IntVar>());
		SelectChoicePoint<IntVar> select = new SimpleSelect<IntVar>(tmp,
				new SmallestDomain<IntVar>(), new IndomainMin<IntVar>());// IndomainMin
		// chooses the lowest value for each variable

		search.getSolutionListener().recordSolutions(true);

		boolean result = search.labeling(store, select, totalWaitingTime);

		if (!result) {
			System.out.println("No solutions found");
			System.out.println(store);
		}
		int counter = 0;
		for (IntVar waitingTime : waitingTimes) {
			System.out.println("Wait time " + counter + ": " + waitingTime);
			counter++;
		}
		System.out.println("Total waiting time: " + totalWaitingTime);

	}

	protected IntVar[][] setupPlaySchedule() {
		pieces = new IntVar[NBR_OF_PIECES][MATRIX_COLUMN];
		for (int i = 0; i < 9; i++) {
			pieces[i] = genRectangle(5, DURATIONS[i], DURATIONS[i]);
			searchVars.add(pieces[i][0]);
		}
		players = new IntVar[NBR_OF_PLAYERS][MATRIX_COLUMN];
		for (int i = 0; i < 5; i++) {
			players[i] = genRectangle(i, 0, LENGTH_OF_SCHEDULE);
			// searchVars.add(players[i][2]);
			// searchVars.add(players[i][0]);
		}

		IntVar[][] rectangles = { pieces[0], pieces[1], pieces[2], pieces[3],
				pieces[4], pieces[5], pieces[6], pieces[7], pieces[8],
				players[0], players[1], players[2], players[3], players[4] };
		playSchedule = new Diff2(rectangles);
		store.impose(playSchedule);
		return rectangles;
	}

	protected IntVar[] genRectangle(int yCoord, int start, int duration) {
		return new IntVar[] { new IntVar(store, 0, LENGTH_OF_SCHEDULE), // x-start
				new IntVar(store, yCoord, yCoord), // y-start
				// x- and y-len variables
				new IntVar(store, start, duration), new IntVar(store, 1, 1) };
	}

	/*
	 * protected IntVar[][] setupCumulativePlaySchedule() { pieces = new
	 * IntVar[NBR_OF_PIECES][MATRIX_COLUMN]; for (int i = 0; i < 9; i++) {
	 * pieces[i] = genTasksDurationsResources(5, DURATIONS[i], DURATIONS[i]);
	 * searchVars.add(pieces[i][0]); } players = new
	 * IntVar[NBR_OF_PLAYERS][MATRIX_COLUMN]; for (int i = 0; i < 5; i++) {
	 * players[i] = genTasksDurationsResources(i, 0, LENGTH_OF_SCHEDULE); //
	 * searchVars.add(players[i][2]); // searchVars.add(players[i][0]); }
	 * 
	 * IntVar[][] rectangles = { pieces[0], pieces[1], pieces[2], pieces[3],
	 * pieces[4], pieces[5], pieces[6], pieces[7], pieces[8], players[0],
	 * players[1], players[2], players[3], players[4] }; playSchedule = new
	 * Diff2(rectangles); store.impose(playSchedule); return rectangles; }
	 * 
	 * protected IntVar[] genTasksDurationsResources(int yCoord, int start, int
	 * duration) { return new IntVar[] { new IntVar(store, 0,
	 * LENGTH_OF_SCHEDULE), // x-start new IntVar(store, yCoord, yCoord), //
	 * y-start // x- and y-len variables new IntVar(store, start, duration), new
	 * IntVar(store, 1, 1) }; }
	 */

	protected void genConstraintsToTrapPieces(int[][] values) {
		for (int i = 0; i < NBR_OF_PLAYERS; i++) {
			IntVar playerSchedEnd = new IntVar(store, 0, LENGTH_OF_SCHEDULE);
			store.impose(new XplusYeqZ(players[i][0], players[i][2],
					playerSchedEnd));
			for (int j = 0; j < NBR_OF_PIECES; j++) {
				IntVar pieceSchedEnd = new IntVar(store, 0, LENGTH_OF_SCHEDULE);
				store.impose(new XplusYeqZ(pieces[j][0], pieces[j][2],
						pieceSchedEnd));

				if (values[i][j] == 1) {
					minPlayerSchedules[i] += DURATIONS[j];

					XgteqY pieceStartBefPlayer = new XgteqY(pieces[j][0],
							players[i][0]);
					XlteqY pieceEndBefPlayer = new XlteqY(pieceSchedEnd,
							playerSchedEnd);

					store.impose(pieceStartBefPlayer);
					store.impose(pieceEndBefPlayer);
				}
			}
			IntVar playerWaitingTime = new IntVar(store, 0, LENGTH_OF_SCHEDULE
					- minPlayerSchedules[i]);
			XplusCeqZ playerTMinusMinTEqWaitingT = new XplusCeqZ(players[i][2],
					-minPlayerSchedules[i], playerWaitingTime);
			store.impose(playerTMinusMinTEqWaitingT);
			waitingTimes.add(playerWaitingTime);
		}
	}

	private IntVar defineTotalWaitingTimeVar() {
		IntVar totalWaitingTime = new IntVar(store, 0, LENGTH_OF_SCHEDULE
				* NBR_OF_PLAYERS);
		store.impose(new Sum(waitingTimes, totalWaitingTime));
		return totalWaitingTime;
	}
}
